package org.gdstash.ui.font;

import javax.swing.*;
import javax.swing.border.Border;
import javax.swing.event.ListSelectionEvent;
import javax.swing.event.ListSelectionListener;
import java.awt.*;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;

public class FontChooserDialog {
    private static final Insets LABEL_INSETS;
    private static final Insets COMBOBOX_INSETS;
    private static final Insets COMBOBOX_INSETS_LAST;
    private static final Insets BUTTON_INSETS;
    private static final String SAMPLE_STRING = "AaBbIiMmYyZz";
    private boolean okPressed;
    private int familySize;
    private int familyStyle;
    private DefaultListModel<String> fontNameListModel;
    private DefaultListModel<String> fontStyleListModel;
    private DefaultListModel<String> fontSizeListModel;
    private EscapeDialog dialog;
    private Font font;
    private JFrame frame;
    private JPanel mainPanel;
    private FontSamplePanel samplePanel;
    private String familyName;
    private TextFieldList fontNameList;
    private TextFieldList fontStyleList;
    private TextFieldList fontSizeList;

    public FontChooserDialog(final JFrame frame) {
        this(frame, null);
    }

    public FontChooserDialog(final JFrame frame, final Font defaultFont) {
        this.frame = frame;
        this.font = defaultFont;
        this.fontNameListModel = new DefaultListModel<String>();
        this.fontStyleListModel = new DefaultListModel<String>();
        this.fontSizeListModel = new DefaultListModel<String>();
        this.getDefaultFontFields();
        this.getAllFontNames();
        this.getAllFontStyles();
        this.getAllFontSizes();
        this.createPartControl();
    }

    public Font getSelectedFont() {
        return this.font;
    }

    public boolean isOkPressed() {
        return this.okPressed;
    }

    private void getDefaultFontFields() {
        if (this.font != null) {
            this.familyName = this.font.getFamily();
            this.familyStyle = this.font.getStyle();
            this.familySize = this.font.getSize();
        }
    }

    private void getAllFontNames() {
        final GraphicsEnvironment e = GraphicsEnvironment.getLocalGraphicsEnvironment();
        for (final String name : e.getAvailableFontFamilyNames()) {
            this.fontNameListModel.addElement(name);
        }
    }

    private void getAllFontStyles() {
        this.fontStyleListModel.addElement("Regular");
        this.fontStyleListModel.addElement("Bold");
        this.fontStyleListModel.addElement("Italic");
        this.fontStyleListModel.addElement("Bold Italic");
    }

    private void getAllFontSizes() {
        for (int i = 8; i <= 11; ++i) {
            this.fontSizeListModel.addElement(Integer.toString(i));
        }
        for (int i = 12; i <= 28; i += 2) {
            this.fontSizeListModel.addElement(Integer.toString(i));
        }
        this.fontSizeListModel.addElement(Integer.toString(36));
        this.fontSizeListModel.addElement(Integer.toString(48));
        this.fontSizeListModel.addElement(Integer.toString(72));
    }

    private void createPartControl() {
        (this.dialog = new EscapeDialog(this.frame)).setModalityType(Dialog.ModalityType.APPLICATION_MODAL);
        this.dialog.setTitle("Font");
        (this.mainPanel = new JPanel()).setLayout(new GridBagLayout());
        int gridy = 0;
        gridy = this.createFontLabelControl(gridy);
        gridy = this.createFontComboBoxControl(gridy);
        gridy = this.createSampleControl(gridy);
        gridy = this.createButtonControl(gridy);
        this.dialog.add(this.mainPanel);
        this.dialog.pack();
        this.dialog.setVisible(true);
    }

    private int createFontLabelControl(int gridy) {
        final JLabel fontNameLabel = new JLabel("Font");
        this.addComponent(this.mainPanel, fontNameLabel, 0, gridy, 1, 1, FontChooserDialog.LABEL_INSETS, 21, 0);
        final JLabel fontStyleLabel = new JLabel("Font style");
        this.addComponent(this.mainPanel, fontStyleLabel, 1, gridy, 1, 1, FontChooserDialog.LABEL_INSETS, 21, 0);
        final JLabel fontSizeLabel = new JLabel("Size");
        this.addComponent(this.mainPanel, fontSizeLabel, 2, gridy++, 1, 1, FontChooserDialog.LABEL_INSETS, 21, 0);
        return gridy;
    }

    private int createFontComboBoxControl(int gridy) {
        final DrawSelectionListener listener = new DrawSelectionListener();
        (this.fontNameList = new TextFieldList(this.fontNameListModel)).addListSelectionListener(listener);
        this.fontNameList.setVisibleRowCount(7);
        this.addComponent(this.mainPanel, this.fontNameList, 0, gridy, 1, 1, FontChooserDialog.COMBOBOX_INSETS, 21, 2);
        (this.fontStyleList = new TextFieldList(this.fontStyleListModel, 75)).addListSelectionListener(listener);
        this.fontStyleList.setVisibleRowCount(7);
        this.addComponent(this.mainPanel, this.fontStyleList, 1, gridy, 1, 1, FontChooserDialog.COMBOBOX_INSETS, 21, 2);
        (this.fontSizeList = new TextFieldList(this.fontSizeListModel, 25)).addListSelectionListener(listener);
        this.fontSizeList.setVisibleRowCount(7);
        this.addComponent(this.mainPanel, this.fontSizeList, 2, gridy++, 1, 1, FontChooserDialog.COMBOBOX_INSETS_LAST, 21, 2);
        if (this.font != null) {
            this.fontStyleList.setSelectedIndex(this.familyStyle);
            this.fontNameList.setSelectedValue(this.familyName);
            this.fontSizeList.setSelectedValue(this.familySize);
        }
        return gridy;
    }

    private int createSampleControl(int gridy) {
        final Border titledBorder = BorderFactory.createTitledBorder("Sample");
        final Border bevelBorder = BorderFactory.createBevelBorder(1);
        final JPanel titlePanel = new JPanel();
        titlePanel.setBorder(titledBorder);
        titlePanel.setLayout(new BorderLayout());
        (this.samplePanel = new FontSamplePanel("AaBbIiMmYyZz")).setBorder(bevelBorder);
        this.samplePanel.setPreferredSize(new Dimension(300, 100));
        titlePanel.add(this.samplePanel, "Center");
        this.addComponent(this.mainPanel, titlePanel, 0, gridy++, 3, 1, FontChooserDialog.COMBOBOX_INSETS_LAST, 21, 2);
        if (this.font != null) {
            this.drawSample();
        }
        return gridy;
    }

    private int createButtonControl(int gridy) {
        final JPanel buttondrawingPanel = new JPanel();
        buttondrawingPanel.setLayout(new FlowLayout());
        final JButton okButton = new JButton("OK");
        okButton.addActionListener(new ActionListener() {
            @Override
            public void actionPerformed(final ActionEvent event) {
                final Font testFont = FontChooserDialog.this.createSelectedFont();
                if (testFont == null) {
                    return;
                }
                FontChooserDialog.this.okPressed = true;
                FontChooserDialog.this.font = testFont;
                FontChooserDialog.this.dialog.setVisible(false);
            }
        });
        this.dialog.setOkButton(okButton);
        buttondrawingPanel.add(okButton);
        final JButton cancelButton = new JButton("Cancel");
        cancelButton.addActionListener(new ActionListener() {
            @Override
            public void actionPerformed(final ActionEvent event) {
                FontChooserDialog.this.okPressed = false;
                FontChooserDialog.this.dialog.setVisible(false);
            }
        });
        buttondrawingPanel.add(cancelButton);
        this.setButtonSizes(okButton, cancelButton);
        this.addComponent(this.mainPanel, buttondrawingPanel, 0, gridy++, 3, 1, FontChooserDialog.BUTTON_INSETS, 21, 2);
        return gridy;
    }

    private void setButtonSizes(final JButton... buttons) {
        Dimension preferredSize = new Dimension();
        for (final JButton button : buttons) {
            final Dimension d = button.getPreferredSize();
            preferredSize = this.setLarger(preferredSize, d);
        }
        for (final JButton button : buttons) {
            button.setPreferredSize(preferredSize);
        }
    }

    private Dimension setLarger(final Dimension a, final Dimension b) {
        final Dimension d = new Dimension();
        d.height = Math.max(a.height, b.height);
        d.width = Math.max(a.width, b.width);
        return d;
    }

    private void addComponent(final Container container, final Component component, final int gridx, final int gridy, final int gridwidth, final int gridheight, final Insets insets, final int anchor, final int fill) {
        final GridBagConstraints gbc = new GridBagConstraints(gridx, gridy, gridwidth, gridheight, 1.0, 1.0, anchor, fill, insets, 0, 0);
        container.add(component, gbc);
    }

    public void drawSample() {
        if (this.samplePanel == null) {
            return;
        }
        this.font = this.createSelectedFont();
        if (this.font == null) {
            return;
        }
        this.samplePanel.setSampleFont(this.font);
    }

    private Font createSelectedFont() {
        boolean allValues = true;
        if (this.fontNameList == null || this.fontNameList.getSelectedValue() == null) {
            allValues = false;
        } else {
            this.familyName = this.fontNameList.getSelectedValue();
        }
        if (this.fontStyleList == null) {
            allValues = false;
        } else {
            this.familyStyle = this.fontStyleList.getSelectedIndex();
        }
        if (this.fontSizeList == null || this.fontSizeList.getSelectedValue() == null) {
            allValues = false;
        } else {
            this.familySize = Integer.parseInt(this.fontSizeList.getSelectedValue());
        }
        if (allValues) {
            return new Font(this.familyName, this.familyStyle, this.familySize);
        }
        return null;
    }

    static {
        LABEL_INSETS = new Insets(10, 10, 0, 0);
        COMBOBOX_INSETS = new Insets(0, 10, 0, 0);
        COMBOBOX_INSETS_LAST = new Insets(0, 10, 0, 10);
        BUTTON_INSETS = new Insets(10, 10, 10, 10);
    }

    public class DrawSelectionListener implements ListSelectionListener {
        @Override
        public void valueChanged(final ListSelectionEvent event) {
            FontChooserDialog.this.drawSample();
        }
    }
}
